﻿#ifndef _COMMAND_H__
#define _COMMAND_H__

#include "protocol/codec.h"
#include "utils/utils.h"
#include "protocol/protocol.h"
#include "protocol/rxmessage.h"
#include <QWaitCondition>
#include <QMutex>
#include <QMetaEnum>

#define response_type(Req) (Req)

#define PLOTDATA_IS_FLOAT(index) ((index>>7) & 0x1)
#define PLATDATA_INDEX(index) (index&0x7F)
struct plotData{
    u8 index;
    u32 data;
};

class Enum : public QObject{
    Q_OBJECT
    public:
        enum ECommand{
            CMD_Log = 0,
            CMD_Start_Motor = 1,
            CMD_Set_CtrlMode,
            CMD_Set_Plot_Type,
            CMD_Set_Target_Torque,
            CMD_Set_Target_Velocity,
            CMD_Set_Target_Vdq,
            CMD_Set_Target_Currdq,
            CMD_Force_Open_Run,
            CMD_Report_Stat,
            CMD_Report_Plot,
            CMD_Number,
            CMD_Cmd_Max = 0xDF,
            CMD_F0 = 0xF0,
            CMD_F1 = 0xF1,
            CMD_F2 = 0xF2,
            CMD_F4 = 0xF4,
            CMD_F5 = 0xF5,
            CMD_F6 = 0xF6,
            CMD_F8 = 0xF8,
            CMD_F9 = 0xF9,
            CMD_FD = 0xFD,
        };
        enum EMode{
            Open = 0,
            Velocity,
            Torque,
            Current,
        };
        Q_ENUM(ECommand)
        Q_ENUM(EMode)
        int value(ECommand c) {
            return (int)c;
        }
        QString name(ECommand c) {
            QMetaEnum meta = QMetaEnum::fromType<ECommand>();
            return meta.valueToKey(c);
        }

        int value(EMode c) {
            return (int)c;
        }
        QString name(EMode c) {
            QMetaEnum meta = QMetaEnum::fromType<EMode>();
            return meta.valueToKey(c);
        }

        int CommandValue(QString key) {
            QMetaEnum meta = QMetaEnum::fromType<ECommand>();
            return meta.keyToValue(key.toStdString().data());
        }
        int ModeValue(QString key) {
            QMetaEnum meta = QMetaEnum::fromType<EMode>();
            return meta.keyToValue(key.toStdString().data());
        }

};

#define COMMAND_STATUS_OK 0
#define COMMAND_STATUS_TIMEOUT 1
#define COMMAND_STATUS_RESULT_ERR 2
#define COMMAND_STATUS_ERR 3

class Command;
class Command:public QObject,public Encoder {
    Q_OBJECT
public:
    Command(Enum::ECommand cmd, int pkg_type = PROT_TYPE_REQUEST, bool dync = false);
    Command(bool dync = false){
        reset();
        b_dync = dync;
        timeout = 100;
    }
    ~Command();
    void Destroy(){
        if (b_dync) {
            delete this;
        }
    }
    Command *with_serial(QObject *sender);
    bool sendSync(bool just_wait = false);
    void sendAsync(bool just_wait = false);

    Command *with_timeout(int timeout) {
        this->timeout = timeout;
        return this;
    }
    Command *with_retry(int retry) {
        this->retry_thro = retry;
        return this;
    }
    Command *with_resp(u16 resp) {
        rsp = resp;
        return this;
    }
    Command *with_noParser() {
        b_No_Parser = true;
        return this;
    }

    virtual bool onParser();
    virtual qint64 onPrepare();

    int error_code(){
        return status;
    }

    bool is_match(RxMessage *message) {
        //qDebug() << "resp " << message->getKey()<<", cmd " << rsp;
        if (rsp == message->getKey()) {
            respData = new RxMessage(message);
            return true;
        }
        return false;
    }

    bool is_expired(){
        return retry_count >= retry_thro;
    }

    bool is_async(){
        return !b_wait;
    }

    void notify_finish(){
        waitCond.wakeOne();
    }
    RxMessage *get_response(){
        return respData;
    }
public:
    Command *next;
    Command *prev;
    qint64 time;
protected:
    RxMessage *respData;
private:
    signals: void send(Command *command);
private:
    head_t head;
    void reset();
    QObject *serial;
    QMutex mutex;
    QWaitCondition waitCond;
    int timeout;
    int retry_thro;
    int retry_count;
    int status;
    u16 rsp;
    bool b_wait;
    bool b_dync;
    bool b_No_Parser;
};

class DataCommand : public Command {
public:
    DataCommand(Enum::ECommand cmd, int addr=0x45);
public:
    virtual bool onParser() {return true;}
};

#endif /* _COMMAND_H__ */
